#include "NetworkServer.hpp"
#include <iostream>

namespace phx
{

static void serverLoop(NetworkServer *server)
{
	while(server->isStarted())
	{
		server->accept();
		server->receive();
	}
	std::this_thread::yield();
}

NetworkServer::NetworkServer(const char *ipAddress, unsigned short port, NetworkType networkType, int maxConnections) : 
	mSocket(UDT::socket(AF_INET, (networkType == NetworkType::Reliable ? SOCK_DGRAM : SOCK_STREAM), 0)),
	mUdtSocketClients(),
	mNetworkType(networkType),
	mStarted(false),
	mThread(nullptr)
{
	memset(&mMy_addr, 0, sizeof(mMy_addr));
	mMy_addr.sin_family = AF_INET;
	mMy_addr.sin_port = htons(port);
	mMy_addr.sin_addr.s_addr = INADDR_ANY;
	//inet_pton(AF_INET, ipAddress, &mMy_addr.sin_addr);

	//memset(&(mMy_addr.sin_zero), '\0', 8);
	bool block = false;
	int blockSet = UDT::setsockopt(mSocket, 0, UDT_RCVSYN, (char*)&block, sizeof(bool));
	if(UDT::bind(mSocket, (sockaddr*)&mMy_addr, sizeof(mMy_addr)) == UDT::ERROR)
	{
		std::cout << "bind: " << UDT::getlasterror().getErrorMessage()<<std::endl;
		// further action may depends on UDT::getlasterror().getErrorCode()
	}
	else
	{
		mStarted = true;
		UDT::listen(mSocket, maxConnections);
		mThread = new std::thread(&serverLoop, this);
		mThread->detach();
	}
}

void NetworkServer::accept()
{
	int namelen;
	sockaddr_in clientData;
	while(true)
	{
		UDTSOCKET client = UDT::accept(mSocket, (sockaddr*)&clientData, &namelen);
		if(client != UDT::INVALID_SOCK)
		{
			UDTSOCKET *newClient = new UDTSOCKET(client);
			//UDT::bind(*newClient, (sockaddr*)&clientData, sizeof(clientData));
			//UDT::connect(*newClient, (sockaddr*)&clientData, namelen);
			mUdtSocketClients.push_back(newClient);

			std::cout<<"client connected: "<<inet_ntoa(clientData.sin_addr)<<std::endl;
		}
		else
		{
			UDT::close(client);
			break;
		}
	}
}

void NetworkServer::send(const char *data, int size)
{
	switch(mNetworkType)
	{
		case NetworkType::Reliable:
			for(std::vector<UDTSOCKET*>::iterator it = mUdtSocketClients.begin(); it != mUdtSocketClients.end(); it++)
			{
				UDT::sendmsg(**it, data, size, -1, true);
			}
			break;
		case NetworkType::Unreliable:
			for(std::vector<UDTSOCKET*>::iterator it = mUdtSocketClients.begin(); it != mUdtSocketClients.end(); it++)
			{
				UDT::send(**it, data, size, 0);
			}
			break;
	}
}

void NetworkServer::send(Packet &packet)
{
	switch(mNetworkType)
	{
		case NetworkType::Reliable:
			for(std::vector<UDTSOCKET*>::iterator it = mUdtSocketClients.begin(); it != mUdtSocketClients.end(); it++)
			{
				UDT::sendmsg(**it, packet.mData.c_str(), packet.mData.size(), -1, true);
			}
			break;
		case NetworkType::Unreliable:
			for(std::vector<UDTSOCKET*>::iterator it = mUdtSocketClients.begin(); it != mUdtSocketClients.end(); it++)
			{
				UDT::send(**it, packet.mData.c_str(), packet.mData.size(), 0);
			}
			break;
	}
}

void NetworkServer::receive()
{
	int rcv_size;
	int var_size = sizeof(int);
	int resultInfo;
	switch(mNetworkType)
	{
		case NetworkType::Reliable:
			for(std::vector<UDTSOCKET*>::iterator it = mUdtSocketClients.begin(); it != mUdtSocketClients.end(); it++)
			{
				UDT::getsockopt(**it, 0, UDT_RCVDATA, &rcv_size, &var_size);
				resultInfo = UDT::recvmsg(**it, mReceivedData, 255);
				if(resultInfo != UDT::ERROR && rcv_size > 0)
				{
					char *d = new char[resultInfo];
					memcpy(d, mReceivedData, resultInfo);
					// The packet has been given the resposibility of deleting "d"
					Packet packet;
					packet.setData(d, resultInfo);

					std::string eventName;
					packet.extract(eventName);
					Event::trigger(eventName, packet);
				}
			}
			break;
		case NetworkType::Unreliable:
			for(std::vector<UDTSOCKET*>::iterator it = mUdtSocketClients.begin(); it != mUdtSocketClients.end(); it++)
			{
				UDT::getsockopt(**it, 0, UDT_RCVDATA, &rcv_size, &var_size);
				resultInfo = UDT::recv(**it, mReceivedData, 255, 0);
				if(resultInfo != UDT::ERROR && rcv_size > 0)
				{
					char *d = new char[resultInfo];
					memcpy(d, mReceivedData, resultInfo);
					// The packet has been given the resposibility of deleting "d"
					Packet packet;
					packet.setData(d, resultInfo);

					std::string eventName;
					packet.extract(eventName);
					Event::trigger(eventName, packet);
				}
			}
			break;
	}
}

NetworkServer::~NetworkServer()
{
	if(mThread)
	{
		mStarted = false;
		delete mThread;
	}
	for(std::vector<UDTSOCKET*>::iterator it = mUdtSocketClients.begin(); it != mUdtSocketClients.end();)
	{
		UDT::close(**it);
		delete *it;
		it = mUdtSocketClients.erase(it);
	}
	mUdtSocketClients.clear();
	UDT::close(mSocket);
}

}